Skip to content

fix(sqlite): read generated columns with pragma_table_xinfo (weasel#426) - #433

Merged
jeremydmiller merged 1 commit into
masterfrom
fix/426-sqlite-generated-columns
Aug 10, 2026
Merged

fix(sqlite): read generated columns with pragma_table_xinfo (weasel#426)#433
jeremydmiller merged 1 commit into
masterfrom
fix/426-sqlite-generated-columns

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #426.

pragma_table_info omits generated columns entirely, so a Table declaring one was read back without it: the delta reported the column missing on every run, emitted ALTER TABLE ... ADD COLUMN, and the second migration failed with duplicate column name. Such a table never converged.

The read

Both introspection sites (ConfigureQueryCommand and FetchExistingAsync) now share one query against pragma_table_xinfo. Its first six columns are table_info's in the same order, so the positional reader in readColumnsAsync is untouched.

Two deliberate details beyond the issue's sketch:

  • Columns are listed explicitly instead of SELECT *, since the reader is positional.
  • hidden <> 1 filters out a virtual table's hidden columns, which table_xinfo reports and table_info never did — for an fts5 table that is the table-name column and rank. Verified: table_xinfo('search') on fts5(title, body) returns title, body, search(hidden=1), rank(hidden=1).

Two consequences for migration

Reading generated columns properly exposes two paths that were only accidentally safe while those columns were invisible:

  • Table recreation copied every column present on both sides. Generated columns are now on both sides, and SQLite refuses writes to them, so the INSERT ... SELECT would fail. They are excluded; the value re-derives from the base columns that do get copied.
  • A newly declared STORED generated column cannot be introduced with ALTER TABLE ADD COLUMN — SQLite answers cannot add a STORED column — so it now forces a recreation. VIRTUAL still takes the incremental path.

Not in scope

The generation expression is still not round-tripped, so a changed expression is not detected. TableColumn.Equals compares name and raw type for every column, not just generated ones, so making expressions authoritative here would be a lopsided change. Documented in docs/sqlite/tables.md instead.

Separately, and pre-existing: Weasel.Sqlite cannot introspect a virtual table at all — fts5 columns report an empty type, which TableColumn's constructor rejects. Unchanged by this PR, and noted in the test that covers the hidden filter.

Tests

8 new tests in GeneratedColumnDeltaTests, 6 of which fail on master for the reasons above. Full SQLite suite: 401 passing.

🤖 Generated with Claude Code

pragma_table_info omits generated columns entirely, so a Table declaring
one was read back without it. The delta reported the column missing on
every run, emitted ALTER TABLE ADD COLUMN, and the second migration
failed with "duplicate column name" -- such a table never converged.

Both introspection sites now query pragma_table_xinfo, whose first six
columns are table_info's in the same order, so the positional reader is
unchanged. The columns are listed explicitly rather than SELECT *, and
hidden = 1 rows are filtered out so the switch does not also start
reporting a virtual table's hidden columns as real ones.

Reading generated columns properly has two consequences for migration:

- Table recreation copied every column present on both sides, which now
  includes generated columns, and SQLite refuses writes to those. They
  are left out of the INSERT ... SELECT; the value re-derives from the
  base columns that are copied.

- A newly declared STORED generated column cannot be introduced with
  ALTER TABLE ADD COLUMN ("cannot add a STORED column"), so it forces a
  recreation. VIRTUAL still takes the incremental path.

The generation expression is still not round-tripped, so a *changed*
expression is not detected -- TableColumn.Equals compares name and type
only, for every column. Documented rather than fixed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit 5d5073b into master Aug 10, 2026
16 checks passed
@jeremydmiller
jeremydmiller deleted the fix/426-sqlite-generated-columns branch August 10, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Weasel.Sqlite: pragma_table_info does not list generated columns, so a table with one never converges

1 participant